# -*- coding: utf-8 -*-
# created on 2016/11/2
from __future__ import division
from collections import defaultdict
from itertools import permutations
from numpy import arange
from sympy import (Function, symbols, gcd, sign, oo, Max, Min, sympify, Intersection, re, fraction, Abs, Symbol, Eq, S,
                   Interval, Union, latex, simplify, FiniteSet, EmptySet)
from sympy.core.relational import Relational
from mathsolver.functions.budengshi.common_opers import ineq_expr_list_2_interval, ineqs_symbs, to_ineq_expr_list
from mathsolver.functions.budengshi.jiebudengshi import JieBuDengShi, MatheMatica
from mathsolver.functions.budengshi.params_ineqs_funcs import BaseParamerInterval
from mathsolver.functions.base import (BaseEqs, BaseEq, BaseFunc, BaseIneqs, BaseBelong, BaseIneq, BaseVariable,
                                       BaseVariables, BaseBelongs, BaseSinFunc, BaseInter, BaseOptimize, BaseNumber,
                                       BaseSinEq, BasePoly, BaseFuncEq)
from mathsolver.functions.hanshu.zhouqi import has_f
from mathsolver.functions.mathematica.mathematicaSolve import (MathematicaOptimize, MathematicaSolve, MathematicaLimit,
                                                               MathematicaSimplify)
from mathsolver.functions.sympy_utils import get_all_child


class JiOu:
    def __init__(self, is_ji=False, is_ou=False):
        self.is_ji = is_ji
        self.is_ou = is_ou
        f, xx = Function('f'), Symbol('x')
        self.property = Eq(f(xx), -f(-xx)) if is_ji else Eq(f(xx), -f(xx))


class DanDiao:
    def __init__(self, qujian=S.Reals, dizen=False, dijian=False):
        self.qujian = qujian
        self.dizen = dizen
        self.dijian = dijian


def create_interval_from_endpoints(value1, value2, open1, open2):
    """根据端点值，还有区间开闭情况，构建区间"""
    d = {sympify(value1): open1, sympify(value2): open2}
    left_value = min(d.keys())
    right_value = max(d.keys())

    left_open = d[left_value] if not left_value.is_infinite else True
    right_open = d[right_value] if not right_value.is_infinite else True
    return Interval(left_value, right_value, left_open, right_open)


def check_func(arg):
    """把 BaseEq 转换成 BaseFunc"""
    if isinstance(arg, (BaseFunc, BaseSinFunc)):
        return arg
    elif isinstance(arg, (BaseEq, BaseFuncEq)):
        return arg.eq2func_xiaofei()
    else:
        raise AssertionError('%s is either BaseEq or BaseFunc' % str(arg))


def check_belong(arg):
    """把 BaseIneq 转换成 BaseBelong
    BaseIneq(['x', '>=', '1']) => BaseBelong(['x', "\\in", Interval(1, oo)])"""
    if isinstance(arg, (BaseBelong, BaseBelongs)):
        return arg
    elif isinstance(arg, (BaseIneq, BaseIneqs)):
        ineq = arg.value

        # 抽取出变量名 x
        symbs = ineqs_symbs(ineq)
        assert len(symbs) == 1
        symb = symbs[0]

        # 转换成区间
        inter = ineq_expr_list_2_interval(ineq)
        return BaseBelong([symb, "\\in", inter])
    else:
        raise ValueError('must be type BaseIneq')


def check_eq(arg):
    """把 BaseFunc 转换成 BaseEq"""
    if isinstance(arg, (BaseFunc, BaseSinFunc)):
        return BaseEq([arg.name, arg.expression])
    elif isinstance(arg, BaseEq):
        return arg
    else:
        raise AssertionError('%s is either BaseEq or BaseFunc' % str(arg))


def check_inter(arg):
    """把 BaseInter 转换成 Interval"""
    if isinstance(arg, (Interval, Union, FiniteSet)):
        return arg
    elif isinstance(arg, BaseInter):
        return arg.interval
    elif isinstance(arg, (BaseIneq, BaseIneqs, BaseSinEq)):
        return ineq_expr_list_2_interval2(arg.value)
    elif isinstance(arg, BaseBelong):
        return arg.interval
    else:
        raise AssertionError('%s is either Interval or BaseInter' % str(arg))


def qujian_gt_zero(qujian):
    """判断区间是否 恒大于 0"""
    return qujian.inf > 0 or (qujian.inf == 0 and 0 not in qujian)


def qujian_lt_zero(qujian):
    """判断区间是否 恒小于 0"""
    return qujian.sup < 0 or (qujian.sup == 0 and 0 not in qujian)


def qujian_get_zero(qujian):
    """判断区间是否 恒大于等于 0"""
    return qujian.inf >= 0


def qujian_let_zero(qujian):
    """判断区间是否 恒小于等于 0"""
    return qujian.sup <= 0


def elim_sign(belong, expr):
    """根据参数取值范围，去除表达式里面的 sign"""
    if not expr.has(sign):
        return expr
    else:
        sign_exprs = get_all_child(expr, lambda xx: isinstance(xx, sign))
        var, interval = belong.var, belong.interval
        lookup_tbl = {}
        for sign_expr in sign_exprs:
            aa = sign_expr.args[0]
            fuhao = eval_sign(aa, var, interval)
            lookup_tbl[sign_expr] = fuhao
        return expr.subs(lookup_tbl)


def eval_sign(expr, var, interval):
    """判断表达式 f(x) 在区间 x ∈ interval 上的符号
    """
    from mathsolver.functions.hanshu.zhiyu import HanShuZhiYu

    # 如果不含有变量，参数
    if not expr.free_symbols:
        if expr > 0:
            return 1
        elif expr < 0:
            return -1
        else:
            return 0

    # 如果表达式含有参数、不含变量
    if not expr.has(var):
        expr_range = HanShuZhiYu().solver(BaseEq(['y', expr])).output[0].value
    else:
        expr_range = HanShuZhiYu().solver(
            BaseFunc({"var": var, "name": "y", "type": "", "expression": expr}),
            interval).output[0].value

    if qujian_gt_zero(expr_range):
        return 1
    elif qujian_lt_zero(expr_range):
        return -1
    elif qujian_get_zero(expr_range):
        return 2
    elif qujian_let_zero(expr_range):
        return -2
    else:
        raise NotImplementedError(" %s 的符号不确定" % latex(expr))


def reverse_op(op):
    """返回与 op 相反的符号"""
    ops = {'<': '>', '>': '<', '<=': '>=', '>=': '<='}
    return ops.get(op, op)


def reverse_zuizhi(zuizhi):
    inverse_zuizhi = {"最大值": "最小值", "最小值": "最大值"}
    return inverse_zuizhi[zuizhi]


def yizhizhiyu_qiufanwei(fx, dd):
    """已知 f(x) ∈ D, 求 x 的范围"""
    if isinstance(dd, Interval):
        lhs, rhs, l_open, r_open = dd.args
        l_op = '<' if l_open else '<='
        r_op = '<' if r_open else '<='
        res = JieBuDengShi().solver(BaseIneqs([lhs, l_op, fx, r_op, rhs])).output[0].value
        res = list(res.values())[0]
        return res
    elif isinstance(dd, Union):
        res = Union(yizhizhiyu_qiufanwei(fx, interval) for interval in dd.args)
        return res
    elif isinstance(dd, FiniteSet):
        return dd
    else:
        raise ValueError("unknown interval %s" % dd)


def is_even(xx):
    """x 是否为偶数"""
    return xx % 2 == 0


def is_odd(xx):
    """x 是否为奇数"""
    return xx % 2 != 0


def simplify_power(expr):
    """[f(x)**(m/n)]**(p/q) 化简为 f(x)**((m/n)*(p/q))
    只有当 m, q 为偶数， m/gcd(m,q), q/gcd(m,q) 都为奇数的时候不能化简，其他形式都可以
    """
    fx_pow, p_div_q = expr.args
    fx, m_div_n = fx_pow.args
    p, q = fraction(p_div_q)
    mm, n = fraction(m_div_n)
    gcd_mq = gcd(mm, q)

    if is_even(mm) and is_even(q) and is_odd(q / gcd_mq) and is_odd(mm / gcd_mq):
        res = Abs(fx) ** (m_div_n * p_div_q)
    else:
        res = fx ** (m_div_n * p_div_q)

    return res


def huajian(expr):
    """化简，针对 sympy 不能处理的[f(x)**(m/n)]**(p/q) 特殊处理"""
    expr = simplify(expr)

    # 抽取出所有 [f(x)**(m/n)]**(p/q) 的表达式
    pows = get_all_child(expr, lambda arg: arg.is_Pow and arg.args[0].is_Pow)
    for p in pows:
        expr = expr.subs(p, simplify_power(p))

    return expr


def is_real(expr):
    """expr 是否是实数"""
    from sympy import I
    return not expr.has(I)


def is_real_mapping(mapping):
    """判断 mapping 的每个 value 是否是实数"""
    return all(is_real(val) for val in mapping.values())


def ineq_equals(ineq1, ineq2):
    """判断两个等式是否相等 f(-3) > -f(2) == -f(-3) < f(2)
    ineq1, ineq2 Relational 类型"""
    func1 = ineq1.func
    func2 = ineq2.func
    if func1 != func2:
        ineq2 = ineq2.reversed
    return func1 == ineq2.func and (ineq1.lhs - ineq1.rhs) == (ineq2.lhs - ineq2.rhs)


def dairu_huajian(eq, item):
    """ f(x) = expr, 化简 item

    例如：
    eq = [f(x), expr], item = x*f(x) + f(x+1) + 3*x"""
    f, expr = eq
    var = f.args[0]
    fxs = get_all_child(item, has_f)
    if fxs:
        for fx in fxs:
            inner_fx = fx.args[0]
            new_expr = expr.subs(var, inner_fx)
            item = item.subs(fx, new_expr)
    return item


def interval_to_baseineq(xx, inter):
    """Interval(a, b) 转化成 BaseIneq 或者 BaseIneqs"""
    assert isinstance(inter, Interval)

    def is_infini(arg):
        return arg in [oo, -oo]

    left, right, l_open, r_open = inter.args
    if is_infini(left):
        if r_open:
            res = BaseIneq([xx, '<', right])
        else:
            res = BaseIneq([xx, '<=', right])
    elif is_infini(right):
        if l_open:
            res = BaseIneq([xx, '>', left])
        else:
            res = BaseIneq([xx, '>=', left])
    else:
        ineqs = []
        if r_open:
            ineqs.append([xx, '<', right])
        else:
            ineqs.append([xx, '<=', right])
        if l_open:
            ineqs.append([xx, '>', left])
        else:
            ineqs.append([xx, '>=', left])
        res = BaseIneqs(ineqs)
    return res


def get_symmetric_interval(interval):
    """求出 interval 关于 0 的对称区间"""
    left_val, right_val, l_open, r_open = interval.args
    symmetric_interval = Interval(-right_val, -left_val, l_open, r_open)
    return symmetric_interval


def my_groupby(iterable, key):
    """groupby elements in iterable by key"""
    res = defaultdict(list)
    if key is None:
        key = lambda xx: xx

    for item in iterable:
        keyvalue = key(item)
        res[keyvalue].append(item)

    return res


def interval_2_str(interval, e):
    """将区间转换成 Mathematica 的表达式 e.g. '-3 <= x < 0 || x > 1'"""
    if isinstance(interval, Interval):
        if interval.left != -S.Infinity and interval.right != S.Infinity:
            res = str(interval.left) + (' < ' if interval.left_open else ' <= ') + e \
                  + (' < ' if interval.right_open else ' <= ') + str(interval.right)
        elif interval.left == -S.Infinity:
            res = e + (' < ' if interval.right_open else ' <= ') + str(interval.right)
        else:
            res = e + (' > ' if interval.left_open else ' >= ') + str(interval.left)
    elif isinstance(interval, Union):
        res = ' || '.join(interval_2_str(item, e) for item in interval.args)
    else:
        raise ValueError('interval must be of type Interval or Union')

    return res


def math_max(expr, interval=None):
    """调用 Mathematica 计算 expr 在区间 interval 上的最大值"""
    max_y, max_xy_dict, reacheable = MathematicaOptimize().solver(
        BaseOptimize(expr, "Maximize", interval), None).output

    return max_y.sympify(), max_xy_dict, reacheable


def math_min(expr, interval=None):
    """调用 Mathematica 计算 expr 在区间 interval 上的最小值"""
    min_y, min_xy_dict, reacheable = MathematicaOptimize().solver(
        BaseOptimize(expr, "Minimize", interval), None).output

    return min_y.sympify(), min_xy_dict, reacheable


def math_range(expr, interval=None):
    """调用 Mathematica 计算 expr 在区间 interval 上的值域"""
    max_y, max_xy, reacheable = math_max(expr, interval)
    min_y, min_xy, reacheable = math_min(expr, interval)
    return Interval(min_y, max_y, not reacheable, not reacheable)


def check_jiou(eq):
    """根据 等式关系判断奇偶性"""
    if eq == sympify('(f(x), f(-x))'):
        return "偶函数"
    elif eq == sympify('(f(x), -f(-x))'):
        return "奇函数"
    elif eq in ["偶函数", "奇函数"]:
        return eq
    else:
        raise ValueError('eq 必须是奇偶函数的定义式')


def base_symbol_value_transform(base_symbol_output):
    """从 BaseSymbolValue 输出值转换成方便处理的格式
    {(b, a): {(1/8, 2)}} => {b: 1/8, a: 2}
    """
    assert isinstance(base_symbol_output, dict)
    pass


def qujian_duandian(qujian):
    """计算区间端点 (0, 1) -> {0, 1}, (0, 1) ∪ (4, 5) -> {0, 1, 4, 5}"""
    if isinstance(qujian, Interval):
        return {qujian.start, qujian.end}
    elif isinstance(qujian, Union):
        res = set()
        for inter in qujian.args:
            res.update([inter.start, inter.end])
        return res
    else:
        raise ValueError("input must has type Interval or Union")


def my_max_min(iterable, canshu, canshu_interval, min_max):
    """计算 iterable [a, b, c] 的最大/小值，可能含有参数，此时需要根据给定参数的取值区间，对 iterable 的各项进行讨论：
    :param iterable 需要比较大小的值
    :param canshu 参数名 sympy 格式
    :param canshu_interval 参数取值范围
    :param min_max Min/Max
    :return 返回 iterable 里面的 Max/Min 值，如果需要讨论参数范围输出 [[val1, Interval1], [val2, Interval2], ...]"""
    result = min_max(*iterable)
    helper_map = {Max: ">=", Min: "<="}

    # 如果没有输出最值，需要讨论参数范围
    if isinstance(result, (Min, Max)):
        values = result.args

        # 计算 a >= b, a >= c; b >= a, b >= c; c >= a, c >= b
        permut = permutations(values, 2)  # [(a, b), (a, c), (b, a), (b, c), (c, a), (c, b)]
        # [((a, b), (a, c)), ((b, a), (b, c)), ((c, a), (c, b))]
        permut_combine = zip(*([iter(permut)] * (len(values) - 1)))
        result_list = []
        for comb in permut_combine:
            # ((a, b), (a, c))
            ineq = [[item1, helper_map[min_max], item2] for item1, item2 in comb]
            # print ineq
            solver = JieBuDengShi().solver(
                BaseIneqs(ineq), BaseVariable(canshu), {canshu: canshu_interval}).output[0].value
            canshu_range = solver.get(canshu)

            if not canshu_range:  # 无解
                continue

            if not isinstance(canshu_range, FiniteSet):
                result_list.append([canshu_range, item1])
                # print "当 %s ∈ %s 时，%s 值为 %s" % (str(canshu), canshu_range, str(min_max), item1)

        return result_list
    else:
        # print "函数的 %s 值为 %s" % (str(min_max), result)
        return [[canshu_interval, result]]


def my_max(iterable, canshu, canshu_interval):
    """计算 iterable 里面的最大值"""
    return my_max_min(iterable, canshu, canshu_interval, Max)


def my_min(iterable, canshu, canshu_interval):
    """计算 iterable 里面的最小值"""
    return my_max_min(iterable, canshu, canshu_interval, Min)


def solveset(ineq_or_eq, symbol=None, *args, **kwargs):
    """解不等式：替换 sympy 的 solveset 函数（很多解不等式错误）
    :param ineq_or_eq sympy 格式的不等式 exp(x) > 1，如果是解不等式组输入格式为 [ineqality1, inequality2, ...]
        如果是 exp(x) -1 的格式则解等式 exp(x) - 1 = 0，如果是解等式组输入格式为 [expr1, expr2]
        
    如果 dict=True, 输出 BaseSymbolValue 里面的 dict 
        
    :param symbol 变量
    :param args sympy solveset 的参数
    :param kwargs sympy solveset 的参数"""
    from mathsolver.functions.sanjiao.sanjiao_utils import is_trig_f, solve_trig_eq
    input_ineq = True

    if 'dict' in kwargs.keys():
        dict_ = kwargs.get('dict')
    else:
        dict_ = False

    # 解等式组/不等式组
    is_sanjiao = False
    if isinstance(ineq_or_eq, list):
        # 等式组
        if isinstance(ineq_or_eq[0], Eq):
            tran = [[eq.args[0] - eq.args[1], 0] for eq in ineq_or_eq]
            input_ineq = False
        # 解不等式组
        else:
            tran = [to_ineq_expr_list(str(ineq)) for ineq in ineq_or_eq]

    # 解等式
    else:
        # 补充如果是三角函数
        is_sanjiao = is_trig_f(ineq_or_eq)
        if isinstance(ineq_or_eq, Relational):
            tran = [to_ineq_expr_list(str(ineq_or_eq))]
        else:
            tran = [[str(ineq_or_eq), 0]]
            input_ineq = False

    # 如果是解不等式（组）
    if input_ineq:
        ineq_solver = MatheMatica().solver(BaseIneqs(tran)).output[0]
        # 如果含参数：
        if isinstance(ineq_solver, BaseParamerInterval):
            return ineq_solver.value['solveset']
        else:
            return list(ineq_solver.value.values())[0]

    # 解等式（组）
    else:
        # 补充如果是三角函数
        if is_sanjiao:
            assert len(tran) == 1, "目前只考虑三角等式，不等式需要补充"
            sanjiao_symbol = symbol if symbol else 'x'
            # 输出格式： [2*pi*k, 2*pi*k + pi]
            sanjiao_solver = solve_trig_eq(BaseEq(tran[0]), x_symbol=sanjiao_symbol)
            return FiniteSet(*sanjiao_solver)

        if symbol:
            symbol_list = symbols if isinstance(symbol, list) else [symbol]
            eq_solver = MathematicaSolve().solver(BaseEqs(tran), BaseVariables(symbol_list))
        else:
            eq_solver = MathematicaSolve().solver(BaseEqs(tran))

        if dict_:
            return eq_solver.output[0].value
        else:
            return list(eq_solver.output[0].value.values())[0]


def get_rand_from_interval(interval):
    """
    产生输入区间内的随机整数
    :param interval: 区间
    :return: 随机数
    """
    if isinstance(interval, Union):
        interval = interval.args[0]
    if isinstance(interval, FiniteSet):
        return interval.args[0]

    lowerbound = interval.args[0]
    upperbound = interval.args[1]

    if interval is S.EmptySet:
        return None
    elif interval == S.Reals:
        return 0
    elif lowerbound == -oo:
        return upperbound - 1
    elif upperbound == oo:
        return lowerbound + 1
    else:
        return (upperbound - lowerbound) / 2


def get_randn_from_interval(interval, n):
    """
    产生输入区间内的 n 个数
    :param interval: 区间
    :param n:
    :return: [n个数]
    """
    half_n = int(n / 2)

    if interval is S.EmptySet:
        return None
    elif isinstance(interval, Interval):
        lowerbound = interval.args[0]
        upperbound = interval.args[1]

        if interval == S.Reals:
            return arange(-half_n, half_n)
        if lowerbound == -oo:
            return arange(upperbound - n, upperbound - 0.9, 1)
        if upperbound == oo:
            return arange(lowerbound + 1, lowerbound + n + 0.1, 1)
        else:
            return arange(lowerbound + 0.1, upperbound, (upperbound - lowerbound) / n)
    elif isinstance(interval, Union):
        return get_randn_from_interval(interval.args[0], n)


def process_m_eqsolver_output(m_output, var_symbol):
    """处理 Mathematica 解含参数等式的结果，合并相同参数取值的 x 值
    例如：{(x, a): {(2, (1, oo)), (2*a, (1, oo))}} => {(a, x): [((1, oo), set([2, 2*a]))]} } 
    {m: {0}, (m, x): {((-oo, 0), -2), ((0, oo), -2)}} => {m: set([0]), (m, x): [((-oo, 0), -2), ((0, oo), -2)]}
    :param var_symbol sympy 格式的 symbol
    :param m_output Mathematica 的输出 dict 格式"""

    assert isinstance(m_output, dict)

    m_output_new = {}
    # {m: {0}, (m, x): {((-oo, 0), -2), ((0, oo), -2)}}
    for prmt, vlus in m_output.items():
        # (m, x): {((-oo, 0), -2), ((0, oo), -2)} 格式
        prm_vlu_dict = defaultdict(set)
        if isinstance(prmt, tuple):
            canshu_symbol = [symb for symb in prmt if symb != var_symbol][0]
            for vlu in vlus:
                prmt_vlu = dict(zip(prmt, vlu))
                prmt2, vlu2 = prmt_vlu[canshu_symbol], prmt_vlu[var_symbol]
                prm_vlu_dict[prmt2].add(vlu2)

            # prm_vlu_dict = { (1, oo) : {2, 2*a} } / { (-oo, 0) : {-2}, (0, oo) : {-2} }
            prm_vlu_list = []
            for prm, vlu in prm_vlu_dict.items():
                if len(vlu) == 1:
                    vlu = vlu.pop()
                prm_vlu_list.append((prm, vlu))

            m_output_new[(canshu_symbol, var_symbol)] = prm_vlu_list
        else:
            m_output_new[prmt] = vlus

    return m_output_new


def isclose(aa, b, rel_tol=1e-09, abs_tol=0.0):
    """compare floating point value a and b"""
    return abs(aa - b) <= max(rel_tol * max(abs(aa), abs(b)), abs_tol)


def allclose(a_list, b_list, rel_tol=1e-09, abs_tol=0.0):
    """Returns True if two arrays are element-wise equal within a tolerance."""
    res = all(isclose(aa, b, rel_tol, abs_tol) for aa, b in zip(a_list, b_list))
    return bool(res)


def cal_canshu_from_expr(expr):
    """从 sympy 表达式 expr 中计算参数符号"""
    sorted_symbol = [fuhao for fuhao in expr.free_symbols
                     if fuhao not in [Symbol('x'), Symbol('y'), Symbol('z')]]
    sorted_symbol.sort(key=lambda arg: str(arg))
    return sorted_symbol[0]


def cal_canshu_from_base_eq_ineq(base_eq_ineq):
    """从 sympy 表达式 BaseEq/BaseIneq 中计算参数符号"""
    assert isinstance(base_eq_ineq, (BaseEq, BaseIneq))
    lexpr, opexpr, rexpr = base_eq_ineq.value
    expr = sympify(lexpr) - sympify(rexpr)
    return cal_canshu_from_expr(expr)


def intersection2(*args):
    """返回区间交集，如果交集为点则返回 EmptySet()"""
    res = Intersection(*args)
    if res.has(FiniteSet):
        children = get_all_child(res, lambda xx: isinstance(xx, FiniteSet))
        return res.subs({fs: EmptySet() for fs in children})
    else:
        return res


def flatten(container):
    """flatten arbitrarily nested lists"""
    for i in container:
        if isinstance(i, (list, tuple, set, FiniteSet)):
            for j in flatten(i):
                yield j
        else:
            yield i


def span_values_to_intervals(values):
    """把点集拓展成区间
    {a, b} => [(-oo, a), {a}, (a, b), {b}, (b, oo)]
    :param values iterable of values
    :return a list of spaning intervals"""
    # 去除 values 里面的 oo, -oo
    values = [val for val in values if val not in [oo, -oo]]
    values_set = FiniteSet(*values)
    values_list = [FiniteSet(e) for e in values_set]
    intervals_list = list(values_set.complement(S.Reals).args)
    result = [None] * (len(values_list) + len(intervals_list))
    result[::2] = intervals_list
    result[1::2] = values_list
    return result


def simplify_budengshi_item(expr):
    """化简 expr >= 0 左边的 expr，如果 expr 含有 () * E**x， () * ()**2，() * ()**(-2)这类项"""
    from sympy import factor, Mul, exp, E
    expr_f = factor(expr)
    changed = False
    if expr_f.func == Mul:
        for arg in expr_f.args:
            # E**x 或者 ()**2 或者 ()**(-2)
            if arg.is_Pow and (arg.args[1] in [2] or arg.args[0] == E):
                expr_f /= arg
                changed = True
            if arg.func == exp:
                expr_f /= arg
                changed = True
    if changed:
        return expr_f
    else:
        return expr


def ineq_expr_list_2_interval2(ineq_expr_list):
    """
    将不等式结构列表转变成Interval结构，在兴发的基础上补充，可以处理 BaseIneqs.value
    :param ineq_expr_list:
    Example:
     ineq_expr_list_2_interval(['5', '<=', 'a', '<=', '10']) => [5, 10]
     ineq_expr_list_2_interval(['a', '>', '5']) => (5, oo)
     ineq_expr_list_2_interval([['a', '>', '5'], ['a', '<', '9']]) => (5, 9)
    """
    # from mathsolver.functions.budengshi.common_opers import
    if isinstance(ineq_expr_list[0], list):
        return Intersection(*[ineq_expr_list_2_interval(item) for item in ineq_expr_list])
    else:
        return ineq_expr_list_2_interval(ineq_expr_list)


def elim_re(expr):
    """去除表达式中的 re(*) => *"""
    re_parts = get_all_child(expr, lambda xx: isinstance(xx, re))
    if re_parts:
        for re_part in re_parts:
            expr = expr.subs(re_part, re_part.args[0])
    return expr


def elim_0power(expr):
    """去除表达式中的 0**(*) """
    zeropower_parts = get_all_child(expr, lambda xx: xx.is_Pow and xx.args[0] == 0)
    if zeropower_parts:
        for zeropower_part in zeropower_parts:
            expr = expr.subs(zeropower_part, 0)
    return expr


def get_number_base_num_eq(arg):
    """在 x=1, 在 1 处"""
    if isinstance(arg, BaseEq):
        value = arg.sympify()[1]
    elif isinstance(arg, BaseNumber):
        value = arg.sympify()
    else:
        raise RuntimeError("arg must be BaseEq/BaseNumber ")

    return value


def limit(expr, var, value, dir='Automatic', assumptions=None):
    """利用 Mathematica 计算函数极限；assumptions 参数范围，可以是 BaseBelong, BaseBelongs, dict 类型"""
    if assumptions:
        if isinstance(assumptions, BaseBelong):
            assumptions = {assumptions.var: assumptions.interval}
        elif isinstance(assumptions, BaseBelongs):
            assumptions = assumptions.get_var_interval_dict
        elif isinstance(assumptions, dict):
            pass
        else:
            raise TypeError('unknow type assumptions')

        result = MathematicaLimit().solver(expr, var, value, direction=dir, assumption=assumptions)
    else:
        result = MathematicaLimit().solver(expr, var, value, direction=dir)

    return result.output[0].value


def m_simplify(expr):
    """利用 Mathematica 化简表达式"""
    return MathematicaSimplify().solver(BasePoly(str(expr))).output[0].sympify()


if __name__ == '__main__':
    pass
